'''
版权：Copyright (c) 2019 China

创建日期：Wednesday November 6th 2019
创建者：ymq(ymq) - <<email>>

修改日期: Wednesday, 6th November 2019 4:23:06 pm
修改者: ymq(ymq) - <<email>>

说明
 1、app支付相关接口
'''
import uuid
from datetime import datetime

import pandas as pd

from server.pao_python.pao.service.data.mongo_db import (C, F, MongoFilter,
                                                         MongoService, N,
                                                         as_date)

from ...service.buss_pub.bill_manage import (BillManageService, OperationType,
                                             Status, TypeId)
from ...service.constant import (AccountStatus, AccountType, PayStatue,
                                 PayType, plat_id)
from ...service.mongo_bill_service import MongoBillFilter


class FinanicalAppPay(MongoService):

    process_collection = 'PT_Financial_Receivable_Process'
    account_collection = 'PT_Financial_Account'
    record_collection = 'PT_Financial_Account_Record'
    divide_collection = 'PT_Financial_Divide_Record'
    user_collection = 'PT_User'

    def __init__(self, db_addr, db_port, db_name, db_user, db_pwd,inital_password, session):
        self.db_addr = db_addr
        self.db_port = db_port
        self.db_name = db_name
        self.db_user = db_user
        self.db_pwd = db_pwd
        self.inital_password = inital_password
        self.session = session
        self.bill_manage_server = BillManageService(
            self.db_addr, self.db_port, self.db_name, self.db_user, self.db_pwd,self.inital_password, self.session)

    def receive_pay_info(self, pay_info):
        '''接收支付信息
        Args：
        {'payer_id':付款方id,'info':[
                                    {
                                        'amount_total':单笔项目金额
                                        ,'subsidy':单笔项目补贴金额
                                        ,'abstract':项目名称
                                        ,'pt_id':平台id
                                        ,'pt_percent':平台分成比例
                                        ,'fw_id':服务商id
                                        ,'fw_percent':服务商分成比例
                                        ,'cs_id':慈善机构id
                                        ,'cs_percent':慈善机构分成比例
                                        ,'remarks'：相关业务信息
                                    }
                                    ,{...}
                                    ,{...}
                                    ]}
        '''
        # 1,查询payer各账户余额
        payer_account_df = pd.DataFrame(self.__get_account_info(pay_info['payer_id']))
        subsidy_balance = payer_account_df[payer_account_df['account_type']
                                           == AccountType.account_subsidy]['balance'].iloc[0]  # 补贴账户余额
        recharg_balance = payer_account_df[payer_account_df['account_type'] ==
                                           AccountType.account_recharg_app]['balance'].iloc[0]  # APP储值账户余额
        subsidy_id = payer_account_df[payer_account_df['account_type']
                                      == AccountType.account_subsidy]['id'].iloc[0]  # 补贴账户id
        recharg_id = payer_account_df[payer_account_df['account_type'] ==
                                      AccountType.account_recharg_app]['id'].iloc[0]  # APP储值账户id
        real_id = payer_account_df[payer_account_df['account_type'] == AccountType.account_real]['id'].iloc[0]  # 真实账户id
        # 2,确定各账户扣款金额
        pay_info_df = pd.DataFrame(pay_info['info'])
        pay_total = sum(pay_info_df['amount_total'].tolist())
        subsidy_total = sum(pay_info_df['subsidy'].tolist())
        if subsidy_total<0 or pay_total<0 :
            return False
        if subsidy_total < subsidy_balance:
            subsidy_pay = subsidy_total
        else:
            subsidy_pay = subsidy_balance  # 补贴账户应扣金额
        other_pay = pay_total-subsidy_pay
        if other_pay < recharg_balance:
            recharg_pay = other_pay
        else:
            recharg_pay = recharg_balance  # app储值账户应扣金额
        real_pay = other_pay-recharg_pay  # 需要调用支付宝或者微信支付的金额
        # 返回各账户的扣款金额
        return {'subsidy': {'id': subsidy_id, 'amount': float(subsidy_pay), 'balance':subsidy_balance}, 'recharg': {'id': recharg_id, 'amount': float(recharg_pay),'balance':recharg_balance}, 'real': {'id': real_id, 'amount': float(real_pay)}}

    def confirm_financial_sys(self, pay_type, pay_info):
        '''支付成功后财务模块相关操作
        Args:
        pay_type:传入值需与paytype的枚举值一致，即“微信支付”和“支付宝支付”，若无需第三方支付则传''
        pay_info：两种情况，
        一种为项目类购买支付，则与receive_pay_info的入参结构完全一致
        {'payer_id':付款方id,'info':[
                                    {
                                        'amount_total':单笔项目金额
                                        ,'subsidy':单笔项目补贴金额
                                        ,'abstract':项目名称
                                        ,'pt_id':平台id
                                        ,'pt_percent':平台分成比例
                                        ,'fw_id':服务商id
                                        ,'fw_percent':服务商分成比例
                                        ,'cs_id':慈善机构id
                                        ,'cs_percent':慈善机构分成比例
                                        ,'remarks'：相关业务信息
                                    }
                                    ,{...}
                                    ,{...}
                                    ]}
        第二种为APP储值账户充值/慈善捐款，入参结构为：
        {'payer_id':付款方id,'info':金额,'abstract':摘要,'remarks':相关业务信息}
        其中，充值操作的摘要内容为充值，慈善捐款的摘要内容为捐款
        '''                                              
        #公共数据
        date = datetime.now()
        pt_account_id = self.__get_main_account_id(plat_id)
        financial_id = str(uuid.uuid1())
        process_common =dict({'financial_id': financial_id, 'pay_status': PayStatue.success}) 
        divide_list = []
        process_list=[]
        account_list=[]
        record_list=[]
        # 充值/捐款成功后操作
        if not isinstance(pay_info['info'], list):
            real_pay_amount= pay_info['info']
            payer_account_df = pd.DataFrame(self.__get_account_info(pay_info['payer_id']))
            payer_real_account_id=payer_account_df[payer_account_df['account_type'] ==
                                                    AccountType.account_real]['id'].iloc[0]
            insert_common={'date': date,'abstract':pay_info['abstract'],'remarks':pay_info['remarks']}
            process_common=dict(process_common,**insert_common)
            # 增加收付款过程记录
            process_list=[dict({'receiver_account_id':pt_account_id
                                ,'pay_account_id':payer_real_account_id
                                ,'amount':real_pay_amount
                                ,'pay_type':pay_type},**process_common)]
            if pay_info['abstract']=='充值':
                payer_account_id = payer_account_df[payer_account_df['account_type'] ==
                                                    AccountType.account_recharg_app]['id'].iloc[0]
                # 账户出入记录
                balance_pay = self.__get_account_balance(payer_account_id)
                record_list=[dict({'account_id':payer_account_id,'amount':real_pay_amount,'balance':balance_pay+real_pay_amount},**insert_common)]
                #账户表余额更新
                account_list=[{'id':payer_account_id,'balance':balance_pay+real_pay_amount}]
            elif pay_info['abstract']=='捐款':
                # 账户出入记录
                balance_pay = self.__get_account_balance(pt_account_id)
                record_list=[dict({'account_id':pt_account_id,'amount':real_pay_amount,'balance':balance_pay+real_pay_amount},**insert_common)]
                #账户表余额更新
                account_list=[{'id':pt_account_id,'balance':balance_pay+real_pay_amount}]
        # 购买服务成功时 
        else :
            pay_amount_info = self.receive_pay_info(pay_info)
            #  对服务项目进行排序(为了按顺序进行补贴扣款)
            info = pd.DataFrame(pay_info['info'])
            info['real_subsidy_pay'] = 0
            info_subsidy = info[info['subsidy'] > 0].sort_values(by='pt_percent', ascending=True)
            info_other = info[info['subsidy'] <= 0]
            if len(info_subsidy)>0:
            #   每笔记录补贴账户支出
                subsidy_pay = pay_amount_info['subsidy']['amount']
                for i in range(len(info_subsidy)):
                    if subsidy_pay > info_subsidy['subsidy'].iloc[i]:
                        info_subsidy['real_subsidy_pay'].iloc[i] = info_subsidy['subsidy'].iloc[i]
                        subsidy_pay = subsidy_pay-info_subsidy['subsidy'].iloc[i]
                    else:
                        info_subsidy['real_subsidy_pay'].iloc[i] = subsidy_pay
                        break
            info_pay = info_subsidy.append(info_other)
            #   每笔记录储值账户支出
            info_pay['recharg_pay'] = 0
            recharg_pay = pay_amount_info['recharg']['amount']
            for i in range(len(info_pay)):
                if recharg_pay > (info_pay['amount_total'].iloc[i]-info_pay['real_subsidy_pay'].iloc[i]):
                    info_pay['recharg_pay'].iloc[i] = info_pay['amount_total'].iloc[i]-info_pay['real_subsidy_pay'].iloc[i]
                    recharg_pay = recharg_pay-(info_pay['amount_total'].iloc[i]-info_pay['real_subsidy_pay'].iloc[i])
                else:
                    info_pay['recharg_pay'].iloc[i] = recharg_pay
                    break
            #   每笔记录真实账户支出
            info_pay['real_pay'] = info_pay['amount_total']-info_pay['real_subsidy_pay']-info_pay['recharg_pay']
            # info_pay的column为：[amount_total,subsidy,abstract,pt_id,pt_percent,fw_id,fw_percent,cs_id,cs_percent,real_subsidy_pay,recharg_pay,real_pay]
            # 1，在过程记录表中记录
            # 各服务商补贴账户过程记录
            bus_subsidy = info_pay[info_pay['real_subsidy_pay'] > 0]
            bus_process_list = []
            if len(bus_subsidy) > 0:
                bus_process_common = dict(
                    process_common, **{'pay_account_id': pay_amount_info['subsidy']['id'], 'pay_type': PayType.subsidy})
                for i in range(len(bus_subsidy)):
                    bus_account_id = self.__get_bus_subsidy_account(bus_subsidy['fw_id'].iloc[i])
                    bus_process_list.append(dict({'receiver_account_id': bus_account_id,
                                                'amount': bus_subsidy['real_subsidy_pay'].iloc[i], 'abstract': bus_subsidy['abstract'].iloc[i],'remarks':bus_subsidy['remarks'].iloc[i]}, **bus_process_common))
            # 平台过程记录
            pt_process_list = []
            #   储值账户
            pt_recharg = info_pay[info_pay['recharg_pay'] > 0]
            if len(pt_recharg) > 0:
                pt_recharg_process_common = dict(
                    {'pay_account_id': pay_amount_info['recharg']['id'], 'pay_type': PayType.recharge_app, 'receiver_account_id': pt_account_id}, **process_common)
                for i in range(len(pt_recharg)):
                    pt_process_list.append(dict(
                        {'amount': pt_recharg['recharg_pay'].iloc[i], 'abstract': pt_recharg['abstract'].iloc[i],'remarks':pt_recharg['remarks'].iloc[i]}, **pt_recharg_process_common))
            #   真实账户
            pt_real = info_pay[info_pay['real_pay'] > 0]
            if len(pt_real) > 0:
                pt_real_process_common = dict(
                    {'pay_account_id': pay_amount_info['real']['id'], 'pay_type': pay_type, 'receiver_account_id': pt_account_id}, **process_common)
                for i in range(len(pt_real)):
                    pt_process_list.append(dict(
                        {'amount': pt_real['real_pay'].iloc[i], 'abstract': pt_real['abstract'].iloc[i],'remarks':pt_real['remarks'].iloc[i]}, **pt_real_process_common))
            process_list=bus_process_list+pt_process_list
            # 2，在分成比例表中记录
            info['un_subsidy_pay'] = info['amount_total']-info['real_subsidy_pay']
            divide_info = info[info['un_subsidy_pay'] > 0]
            if len(divide_info) > 0:
                divide_common = {'financial_id': financial_id, 'is_distributed': False, 'date': date}
                for i in range(len(divide_info)):
                    tep_common = dict({'amount': float(divide_info['un_subsidy_pay'].iloc[i]),
                                    'abstract': divide_info['abstract'].iloc[i],'remarks':divide_info['remarks'].iloc[i]}, **divide_common)
                    tep_divide = {'user_id': divide_info['pt_id'].iloc[i],
                                'divide_percent': float(divide_info['pt_percent'].iloc[i])}
                    divide_list.append(dict(tep_common, **tep_divide))
                    tep_divide = {'user_id': divide_info['fw_id'].iloc[i],
                                'divide_percent': float(divide_info['fw_percent'].iloc[i])}
                    divide_list.append(dict(tep_common, **tep_divide))
                    tep_divide = {'user_id': divide_info['cs_id'].iloc[i],
                                'divide_percent': float(divide_info['cs_percent'].iloc[i])}
                    divide_list.append(dict(tep_common, **tep_divide))
            # 3，在账户出入记录表中记录,账户表余额更新
            subsidy_pay_total = pay_amount_info['subsidy']['amount']
            recharg_pay_total = pay_amount_info['recharg']['amount']
            real_pay_total = pay_amount_info['real']['amount']
            #   付款方
            if subsidy_pay_total > 0:
                pay_account_id = pay_amount_info['subsidy']['id']
                balance_pay = self.__get_account_balance(pay_account_id)
                record_list.append({'account_id': pay_account_id, 'amount': subsidy_pay_total *
                                    (-1), 'balance': balance_pay-subsidy_pay_total,'date':date})
                account_list.append({'id': pay_account_id, 'balance': balance_pay-subsidy_pay_total})
            if recharg_pay_total > 0:
                pay_account_id = pay_amount_info['recharg']['id']
                balance_pay = self.__get_account_balance(pay_account_id)
                record_list.append({'account_id': pay_account_id, 'amount': recharg_pay_total *
                                    (-1), 'balance': balance_pay-recharg_pay_total,'date':date})
                account_list.append({'id': pay_account_id, 'balance': balance_pay-recharg_pay_total,'date':date})
            if real_pay_total > 0:
                pay_account_id = pay_amount_info['real']['id']
                record_list.append({'account_id': pay_account_id, 'amount': real_pay_total * (-1),'date':date})
            #   收款方
            if (recharg_pay_total+real_pay_total) > 0:
                receive_account_id = self.__get_main_account_id(info['pt_id'].iloc[i])
                balance_receive = self.__get_account_balance(receive_account_id)
                record_list.append({'account_id': receive_account_id, 'amount': recharg_pay_total +
                                    real_pay_total, 'balance': balance_receive+recharg_pay_total+real_pay_total,'date':date})
                account_list.append({'id': receive_account_id, 'balance': balance_receive+recharg_pay_total+real_pay_total,'date':date})
            if subsidy_pay_total > 0:
                bus_sub_info = bus_subsidy[['fw_id', 'real_subsidy_pay']]
                bus_sub_info = bus_sub_info.groupby(['fw_id'], as_index=False).sum()
                for i in range(len(bus_sub_info)):
                    receive_account_id = self.__get_bus_subsidy_account(bus_sub_info['fw_id'].iloc[i])
                    balance_receive = self.__get_account_balance(receive_account_id)
                    tep_amount = bus_sub_info['real_subsidy_pay'].iloc[i]
                    record_list.append({'account_id': receive_account_id, 'amount': tep_amount,
                                        'balance': balance_receive+tep_amount,'date':date})
                    account_list.append({'id': receive_account_id, 'balance': balance_receive+tep_amount})
        # 4,完成过程记录表，分成表，出入记录表和账户表的插入及更新,分别对应数据为process_list,divide_list,record_list,account_list
        if  len(process_list)>0 :
            bill_id_process = self.bill_manage_server.add_bill(
                OperationType.add.value, TypeId.financial.value,process_list , self.process_collection)
        if  len(divide_list)>0 :
            bill_id_divide = self.bill_manage_server.add_bill(
                OperationType.add.value, TypeId.financial.value,divide_list , self.divide_collection)
        if  len(record_list)>0 :
            bill_id_record = self.bill_manage_server.add_bill(
                OperationType.add.value, TypeId.financial.value,record_list , self.record_collection)
        if  len(account_list)>0 :
            bill_id_account = self.bill_manage_server.add_bill(
                OperationType.update.value, TypeId.financial.value,account_list , self.account_collection)
        return True


    def __get_account_info(self, user_id):
        '''通过userID查询账户信息'''
        _filter = MongoBillFilter()
        _filter.match_bill((C('user_id') == user_id) & (C('account_status') == AccountStatus.normal))\
               .project({'_id': 0, 'id': 1, 'account_type': 1, 'balance': 1})
        res = self.query(_filter, self.account_collection)
        return res

    def __get_bus_subsidy_account(self, user_id):
        '''通过userID查询服务商补贴账户id'''
        account_df = pd.DataFrame(self.__get_account_info(user_id))
        res = account_df[account_df['account_type'] == AccountType.account_subsidy]['id'].iloc[0]
        return res

    def __get_main_account_id(self, user_id):
        '''通过userID查询组织机构的主账户id'''
        _filter = MongoBillFilter()
        _filter.match_bill(C('id') == user_id)\
               .project({'_id': 0})
        res_df = pd.DataFrame(self.query(_filter, self.user_collection))
        res = res_df['main_account'].iloc[0]
        return res

    def __get_account_balance(self, account_id):
        '''通过account_id查询账户出入表中的最新的余额'''
        _filter = MongoBillFilter()
        _filter.match_bill(C('account_id') == account_id)\
               .sort({'modify_date': -1})\
               .limit(1)
        res_df = pd.DataFrame(self.query(_filter, self.record_collection))
        if len(res_df) == 0:
            return 0
        else:
            return float(res_df['balance'].iloc[0])

    def insert_data(self,data,collection_name):
        self.bill_manage_server.add_bill(
                OperationType.add.value, TypeId.financial.value,data , collection_name)
